#include "workcheck.h"
#include "ui_workcheck.h"
#include <QDebug>
#include <QMessageBox>

using namespace cv;
using namespace std;

extern CascadeClassifier cascade;  //声明同一个分类器对象cascade
CascadeClassifier cascade_2;  //用于检测侧脸

workCheck::workCheck(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::workCheck)
{
    ui->setupUi(this);
    connect(ui->back,&QPushButton::clicked,[=](){
        emit this->back();
    });  //按返回键回到上个界面

    QTimer *pTimer = new QTimer();
    connect(pTimer, SIGNAL(timeout()), this, SLOT(showTime()));
    pTimer->start(100);  //用一个定时信号来更改时间

    //连接compare信号和槽函数
    connect(this,SIGNAL(compare()),this,SLOT(compare_face()));

}

void workCheck::showTime(){
    //获取并在LCD上输出日期和时间
    QDateTime current = QDateTime::currentDateTime();
    ui->timeShow->display(current.toString("yyyy-MM-dd  HH:mm:ss"));
}


workCheck::~workCheck()
{
    delete ui;
}

void workCheck::on_begin_clicked()
{
    //调用摄像头
    VideoCapture video;
    video.open(0);

    stop = false;
    int sig = 0;
    bool check = false;
    bool profile = false;
    chrono::steady_clock::time_point startTime;
    QString closestName;
    QString closestId;


    /*载入已训练的人脸识别算法，具体路径需要根据opencv库安装位置在config.h更改*/
    const string casPath = HAAR_PATH;
    const string casPath2 = HAAR_PATH_2;

    //检测摄像头是否成功打开
    if (!video.isOpened()) {
        qDebug() << "摄像头加载出错！";
        exit(-1);
    }

    //检测是否成功加载级联分类器
    if (!cascade.load(casPath)) {
        qDebug() << "算法加载失败！";
        exit(-1);
    }
    if (!cascade_2.load(casPath2)) {
        qDebug() << "算法加载失败！";
        exit(-1);
    }

    while (stop != true) {
        Mat vClip;
        video >> vClip;  //视频转为图像

        //若突然没有视频信号则结束
        if (vClip.empty()) {
            break;
        }

        //灰度化，减少计算
        Mat imgGray;
        cvtColor(vClip, imgGray, COLOR_BGR2GRAY);

        vector<Rect> faces(0);
        cascade.detectMultiScale(imgGray, faces, 1.1, 2, 0, Size(30, 30));  //调用人脸识别算法

        if (faces.size() > 0) {
            for (size_t i = 0; i < faces.size(); i++) {
                rectangle(vClip, faces[i], Scalar(0, 0, 255), 2, 8, 0);  //生成矩形框出人脸
            }
        }

        //未检测到人脸
        if (faces.size() == 0 && !check && !profile) {
            ui->info->setText("未识别到人脸！");
            sig = 0;
        }

        //不止一张人脸（不能确定谁打卡）
        else if (faces.size() > 1 && !check && !profile) {
            ui->info->setText("请逐个打卡！");
            sig = 0;
        }

        //只有一张人脸
        else if ((faces.size() == 1 || check) && !profile){
            ui->info->setText("请保持不动！");
            sig++;

            //检测到只有一张人脸3秒后进行比对，其间一旦出现不是一张人脸的情况，将重新计时
            if (sig == 1 && !check) {
                startTime = chrono::steady_clock::now();
            }

            else if (sig >= 3 || check) {
                chrono::steady_clock::time_point currentTime = chrono::steady_clock::now();
                chrono::duration<double> interval = currentTime - startTime;

                if (interval.count() >= 3.0 || check) {
                    check = true;
                    ui->info->setText("请稍后！");

                    //调用dlib算法获取特征向量
                    dlib::shape_predictor predictor;
                    dlib::deserialize("D:/tideYH/dlib-19.24/shape_predictor_68_face_landmarks.dat") >> predictor;

                    std::vector<dlib::full_object_detection> face_landmarks;
                    for (size_t i = 0; i < faces.size(); i++) {
                        dlib::rectangle face_rect(faces[i].x, faces[i].y, faces[i].x + faces[i].width - 1, faces[i].y + faces[i].height - 1);
                        dlib::full_object_detection landmarks = predictor(dlib::cv_image<unsigned char>(imgGray), face_rect);
                        face_landmarks.push_back(landmarks);
                    }

                    cv::Mat inputFloat;
                    imgGray.convertTo(inputFloat, CV_32FC1);

                    Mat detect;
                    for (size_t i = 0; i < face_landmarks.size(); i++) {
                        Mat feature(1, 136, CV_32FC1, Scalar::all(0.0));
                        dlib::full_object_detection landmarks = face_landmarks[i];
                        int k = 0;
                        for (int j = 0; j < 68; j++) {
                            feature.at<float>(0, k++) = landmarks.part(j).x();
                            feature.at<float>(0, k++) = landmarks.part(j).y();
                        }
                        detect.push_back(feature);
                    }

                    // 连接数据库
                    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
                    db.setDatabaseName("myDatabase.db");
                    if (!db.open()) {
                        qDebug() << "Failed to establish database connection.";
                        return;
                    }

                    // 查询特征向量对应的脸是否存在
                    QSqlQuery query;
                    query.prepare("SELECT name, id, face FROM face");
                    if (!query.exec()) {
                        qDebug() << "Error executing query: " << query.lastError();
                        db.close();
                        return;
                    }

                    double minDistance = std::numeric_limits<double>::max();
                    double threshold = 100; // 设置阈值
                    const int DIM = 136;

                    while (query.next()) {
                        QString name = query.value(0).toString();
                        QString id = query.value(1).toString();
                        QByteArray storedData = query.value(2).toByteArray();



                        cv::Mat storedFace(1, DIM, CV_32F);
                        memcpy(storedFace.ptr(), storedData.data(), DIM * sizeof(float));

                        double distance = cv::norm(detect, storedFace, cv::NORM_L2);

                        if (distance < minDistance&& distance < threshold) {
                            minDistance = distance;
                            closestName = name;
                            closestId = id;
                        }
                    }

                    if (minDistance < std::numeric_limits<double>::max()) {
                        qDebug() << "Face found in database. Closest match: Name:" << closestName << ", ID:" << closestId;
                        ui->name->setText(closestName);
                        ui->id->setText(closestId);
                        profile = true;
                    }
                    else {
                        qDebug() << "Face not found in database.";
                        QMessageBox::information(this, "提示", "考勤失败");
                        db.close();
                        break;
                        // 考勤失败
                    }

                    // 关闭数据库连接
                    db.close();
                }
            }
        }

        else if (profile) {
            ui->info->setText("请转头并保持！");
            vector<Rect> faces_2(0);
            cascade_2.detectMultiScale(imgGray, faces_2, 1.1, 2, 0, Size(30, 30));  //调用人脸识别算法
            if (faces_2.size() == 1 && faces.size() == 0) {
                ui->info->setText("识别成功！");
                check_duplicate(closestName,closestId);
                return;
            }
        }

        //转换图片格式以输出
        Mat temp;
        cvtColor(vClip,temp,COLOR_BGR2RGB);
        QImage Qtemp = QImage((const unsigned char*)(temp.data),
                                temp.cols, temp.rows, temp.step,
                                QImage::Format_RGB888);

        ui->detect->setPixmap(QPixmap::fromImage(Qtemp));  //输出检测后带框的人脸

        waitKey(1); //waitkey 1ms的延迟
        QApplication::processEvents();
    }

}

//compare信号的槽函数，暂时没有用到
void workCheck::compare_face(){

}

void workCheck::check_duplicate(QString name, QString id) {
    // 创建数据库连接
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName("myDatabase.db");
    if (!db.open()) {
        qDebug() << "Failed to establish database connection.";
        return;
    }

    QSqlQuery query;

    // 将当前时间转换成字符串
    QDateTime current = QDateTime::currentDateTime();
    QString currentTime = current.toString("yyyy-MM-dd HH:mm:ss");

    // 检查半小时内是否有重复打卡记录

    query.prepare("SELECT * FROM attendance WHERE name = :name AND id = :id AND time >= datetime(:currentTime, '-30 minutes')");
    query.bindValue(":name", name);
    query.bindValue(":id", id);
    query.bindValue(":currentTime", currentTime);

    if (query.exec() && !query.next()) {
        qDebug() << "No duplicate check-in found. Adding new record.";
        add_card(name, id); // 调用add_card函数添加新记录
    }
    else{
        ui->info->setText("半小时内已打卡！");
        //...
    }

    db.close();

}

void workCheck::add_card(QString name, QString id) {
    // 创建数据库连接
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName("myDatabase.db");
    if (!db.open()) {
        qDebug() << "Failed to establish database connection.";
        return;
    }

    // 创建时间表
    QSqlQuery query;
    if (!query.exec("CREATE TABLE IF NOT EXISTS attendance (name TEXT, id TEXT, time TEXT)")) {
        qDebug() << "Error creating table attendance: " << query.lastError();
        db.close();
        return;
    }

    // 将当前时间转换成字符串
    QDateTime current = QDateTime::currentDateTime();
    QString currentTime = current.toString("yyyy-MM-dd HH:mm:ss");

    // 插入记录
    query.prepare("INSERT INTO attendance (name, id, time) VALUES (:name, :id, :time)");
    query.bindValue(":name", name);
    query.bindValue(":id", id);
    query.bindValue(":time", currentTime);

    if (!query.exec()) {
        qDebug() << "Error inserting record: " << query.lastError();
    }

    db.close();
}

//停止按钮，停止检测，清屏
void workCheck::on_stop_clicked()
{
    stop = true;
    ui->info->clear();
    ui->detect->clear();
}

//返回按钮，停止检测，清屏并返回上一界面
void workCheck::on_back_clicked()
{
    stop = true;
    ui->info->clear();
    ui->detect->clear();
}
